﻿//TODO: EstimateBackground: test, speed up, average color detection correction

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class EstimateBoundingBox : BaseImageFilter
    {
        protected List<Range> m_aRange = new List<Range>();
        protected List<Range> m_aLeftRange = new List<Range>();
        protected List<Range> m_aRightRange = new List<Range>();
        protected Color colorReplace = Color.Transparent;
        protected int m_nDistanceThreshold = 120;

        /// <summary>
        /// Default constructor
        /// </summary>
        public EstimateBoundingBox()
            : this(Color.Blue)
        {

        }

        /// <summary>
        /// Specify the replacing color
        /// </summary>
        /// <param name="_colorReplace"></param>
        public EstimateBoundingBox(Color _colorReplace)
            : this(Color.Blue, 120)
        {
        }

        /// <summary>
        /// Specify replacing color and distance threshold
        /// </summary>
        /// <param name="_colorReplace"></param>
        /// <param name="_nDistanceThreshold"></param>
        public EstimateBoundingBox(Color _colorReplace, int _nDistanceThreshold)
        {
            colorReplace = _colorReplace;
            m_nDistanceThreshold = _nDistanceThreshold;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_estimateBoundingBox"></param>
        internal EstimateBoundingBox(EstimateBoundingBox _estimateBoundingBox)
        {
            this.colorReplace = _estimateBoundingBox.colorReplace;
            this.m_nDistanceThreshold = _estimateBoundingBox.m_nDistanceThreshold;
        }

        /// <summary>
        /// Return all ranges
        /// </summary>
        public List<Range> Ranges
        {
            get { return m_aRange; }
        }

        /// <summary>
        /// Return left ranges
        /// </summary>
        public List<Range> LeftRanges
        {
            get { return m_aLeftRange; }
        }

        /// <summary>
        /// Return right ranges
        /// </summary>
        public List<Range> RightRanges
        {
            get { return m_aRightRange; }
        }

        /// <summary>
        /// Gets or sets distance threshold
        /// </summary>
        public int DistanceThreshold
        {
            get { return m_nDistanceThreshold; }
            set { m_nDistanceThreshold = value; }
        }

        //TODO: improve
        /// <summary>
        /// Tries To mark all bounding box informations
        /// </summary>
        /// <param name="_bitmap"></param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            m_aRange.Clear();
            m_aLeftRange.Clear();
            m_aRightRange.Clear();

            UnsafeBitmap bitmapCloned = new RoundColors(10).ExecuteCloned(_bitmap);
            UnsafeBitmap bitmap = _bitmap;
            Graphics graphics = Graphics.FromImage(bitmap.InternalBitmap);
            int y = 0;
            int x = 0;

            Pen pen = new Pen(new SolidBrush(colorReplace));

            int nAverageR = -1;
            int nAverageG = -1;
            int nAverageB = -1;

            int nCount = 0;

            Color colorAverage = Color.Transparent;
            Color color = Color.Transparent;

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;
            for (y = 0; y < nHeight; y++)
            {
                nAverageR = 0;
                nAverageG = 0;
                nAverageB = 0;
                for (x = nWidth - 1; x >= 0; x--)
                {
                    color = bitmapCloned.GetPixel(x, y);
                    nCount++;
                    nAverageR += color.R;
                    nAverageG += color.G;
                    nAverageB += color.B;

                    colorAverage = Color.FromArgb(nAverageR / nCount, nAverageG / nCount, nAverageB / nCount);

                    _bitmap.UnlockImage();
                    if (color != colorAverage)
                    {
                        int nDistance = ColorFunctions.Distance(color, colorAverage);
                        if (nDistance > m_nDistanceThreshold)
                        {
                            if (colorReplace != Color.Transparent)
                            {
                                graphics.DrawLine(pen, x, y, x + nCount, y);
                            }
                            Range range = new Range(x, x + nCount, y, pen.Color);
                            m_aRange.Add(range);
                            m_aRightRange.Add(range);
                            nAverageR = 0;
                            nAverageG = 0;
                            nAverageB = 0;
                            break;
                        }
                    }
                    _bitmap.LockImage();
                }

                for (x = 0; x < nWidth; x++)
                {
                    color = bitmapCloned.GetPixel(x, y);
                    nCount++;
                    nAverageR += color.R;
                    nAverageG += color.G;
                    nAverageB += color.B;
                    colorAverage = Color.FromArgb(nAverageR / nCount, nAverageG / nCount, nAverageB / nCount);

                    if (color != colorAverage)
                    {
                        int nDistance = ColorFunctions.Distance(color, colorAverage);
                        if (nDistance > m_nDistanceThreshold)
                        {
                            if (colorReplace != Color.Transparent)
                            {
                                _bitmap.UnlockImage();
                                graphics.DrawLine(pen, x - nCount, y, x, y);
                                _bitmap.LockImage();
                            }
                            Range range = new Range(x - nCount, x, y, pen.Color);
                            m_aRange.Add(range);
                            m_aLeftRange.Add(range);
                            nAverageR = 0;
                            nAverageG = 0;
                            nAverageB = 0;
                            break;
                        }
                    }
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Creates a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new EstimateBoundingBox(this);
        }
    }
}
